/*
 * Copyright (c) 2019.  drakelee.base All rights reserved.
 */

package com.base.components.task.controller;

import com.base.components.common.constants.sys.Dates;
import com.base.components.common.constants.sys.Pages;
import com.base.components.common.dto.page.DataPage;
import com.base.components.common.service.task.SysTask;
import com.base.components.common.service.task.SysTaskLog;
import com.base.components.common.util.ConvertUtil;
import com.base.components.common.util.JsonUtils;
import com.base.components.database.dao.base.condition.ConditionEnum;
import com.base.components.database.dao.base.condition.ConditionGroup;
import com.base.components.feign.clients.TaskManagerBaseClient;
import com.base.components.task.service.ServerClientService;
import com.base.components.task.service.SysTaskLogService;
import com.base.components.task.service.SysTaskService;
import com.base.components.task.service.TaskBroadcastType;
import com.google.common.collect.Maps;
import org.apache.commons.collections.map.SingletonMap;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Sort;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * 作业管理 服务
 *
 * @author <a href="drakelee1221@gmail.com">LiGeng</a>
 * @version 1.0.0, 2017-08-03 18:43
 *
 */
@RestController
public class TaskManagerController implements TaskManagerBaseClient {

  @Autowired
  private SysTaskService sysTaskService;
  @Autowired
  private SysTaskLogService sysTaskLogService;
  @Autowired
  private ServerClientService serverClientService;

  /**
   * 添加任务，广播并持久化到数据库
   *
   * @param paramMap -
   * <p> taskName        - Nonnull  - Str  - 作业名称
   * <p> description     - Nullable - Str  - 作业描述
   * <p> targetClass     - Nonnull  - Str  - 作业执行类全名
   * <p> cronExpression  - Nonnull  - Str  - 作业执行周期 <br>
   *  - 格式：[秒] [分] [小时] [日] [月] [周] [年]，如：2016年1月10日13点每分0秒 为 0 * 13 10 1 ? 2016
   * <p> argumentsMap    - Nullable - Str -  作业执行调用方法时传递的参数json <br>
   *  - 格式：{"aaa":"123", "bbb":"321"}
   * <p> async           - Nullable - bool - true=异步广播(默认), false=同步广播
   *
   * @return SysTask
   */
  @Override
  @RequestMapping(path = "/task/add", method = RequestMethod.POST,
    produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
  public SysTask addTask(@RequestParam Map<String, String> paramMap){
    SysTask task = sysTaskService.addTask(paramMap);
    serverClientService.sendBroadcast(task.getId(), TaskBroadcastType.ADD,
                                      ConvertUtil.convert(paramMap.get("async"), Boolean.TRUE));
    return task;
  }

  /**
   * 修改任务，广播并持久化到数据库
   *
   * @param paramMap -
   * <p> taskId          - Nonnull  - Str  - 作业ID
   * <p> taskName        - Nullable - Str  - 作业名称
   * <p> description     - Nullable - Str  - 作业描述
   * <p> targetClass     - Nullable - Str  - 作业执行类全名
   * <p> cronExpression  - Nullable - Str  - 作业执行周期 <br>
   *  - 格式：[秒] [分] [小时] [日] [月] [周] [年]，如：2016年1月10日13点每分0秒 为 0 * 13 10 1 ? 2016 <br>
   *    调度时间配置设置秒位精确度只能为 0 或 00
   * <p> argumentsMap    - Nullable - Str  - 作业执行调用方法时传递的参数json <br>
   *  - 格式：{"aaa":"123", "bbb":"321"}
   * <p> async           - Nullable - bool - true=异步广播(默认), false=同步广播
   *
   * @return SysTask
   */
  @Override
  @RequestMapping(path = "/task/update", method = RequestMethod.PUT,
                  produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
  public SysTask updateTask(@RequestParam Map<String, String> paramMap){
    SysTask task = sysTaskService.updateTask(paramMap);
    serverClientService.sendBroadcast(task.getId(), TaskBroadcastType.RESTART,
                                      ConvertUtil.convert(paramMap.get("async"), Boolean.TRUE));
    return task;
  }

  /**
   * 立即触发任务，并广播
   *
   * @param fireTaskId - Nonnull  - Str  - id
   * @param async - Nullable - bool - true=异步广播,false=同步广播
   * @param argumentsMap - Nullable - Json - 调用参数
   *
   * @return SysTask
   */
  @Override
  @RequestMapping(path = "/task/trigger", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
  public Map trigger(@RequestParam("fireTaskId") String fireTaskId,
                     @RequestParam(value = "async", defaultValue = "true") boolean async,
                     @RequestParam("argumentsMap") String argumentsMap){
    Map<String, String> extendParam = null;
    try {
      JsonUtils.reader(argumentsMap, HashMap.class);
      extendParam = Maps.newHashMap();
      extendParam.put("argumentsMap", argumentsMap);
    } catch (IOException ignored) {
    }
    int count = serverClientService.sendBroadcast(fireTaskId, TaskBroadcastType.TRIGGER, async, extendParam);
    return new SingletonMap("count", count);
  }

  /**
   * 启动任务，广播并持久化到数据库
   *
   * @param fireTaskId - Nonnull  - Str  - id
   * @param async      - Nullable - bool - true=异步广播,false=同步广播
   *
   * @return SysTask
   */
  @Override
  @RequestMapping(path = "/task/start", method = RequestMethod.PUT,
                  produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
  public SysTask startTask(@RequestParam("fireTaskId") String fireTaskId,
                    @RequestParam(value = "async", defaultValue = "true") boolean async){
    SysTask task = sysTaskService.pauseOrStart(fireTaskId, Boolean.FALSE);
    serverClientService.sendBroadcast(fireTaskId, TaskBroadcastType.RESTART, async);
    return task;
  }

  /**
   * 暂停任务，广播并持久化到数据库
   *
   * @param fireTaskId - Nonnull  - Str  - id
   * @param async      - Nullable - bool - true=异步广播,false=同步广播
   *
   * @return SysTask
   */
  @Override
  @RequestMapping(path = "/task/pause", method = RequestMethod.PUT,
                  produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
  public SysTask pauseTask(@RequestParam("fireTaskId") String fireTaskId,
                    @RequestParam(value = "async", defaultValue = "true") boolean async){
    SysTask task = sysTaskService.pauseOrStart(fireTaskId, Boolean.TRUE);
    serverClientService.sendBroadcast(fireTaskId, TaskBroadcastType.PAUSE, async);
    return task;
  }

  /**
   * 重构某服务器所有任务
   *
   * @param async      - Nullable - bool - true=异步广播,false=同步广播
   *
   * @return Boolean
   */
  @Override
  @RequestMapping(path = "/task/rebuild", method = RequestMethod.PUT,
                  produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
  public Boolean rebuildServer(@RequestParam(value = "async", defaultValue = "true") boolean async){
    try {
      serverClientService.sendBroadcast(TaskBroadcastType.REBUILD, async);
    } catch (Exception e) {
      e.printStackTrace();
      return Boolean.FALSE;
    }
    return Boolean.TRUE;
  }

//  /**
//   * 停止某服务器的动态作业功能
//   *
//   * @param serverHost
//   * @param async      true=异步广播,false=同步广播
//   */
//  @RequestMapping(path = "/task/stop", method = RequestMethod.PUT,
//                  produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
//  public Boolean stopServer(@RequestParam("serverHost") String serverHost,
//                     @RequestParam(value = "async", defaultValue = "true") boolean async){
//    return null;
//  }


  /**
   * 查询作业
   *
   * @param paramMap -
   * <p> id             - Nonnull  - Str  - 作业ID
   *
   * @return SysTask;
   */
  @Override
  @RequestMapping(path = "/task/list", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
  public SysTask getTask(@RequestParam Map<String, String> paramMap){
    String id = paramMap.get("id");
    if(StringUtils.isBlank(id)){
      return null;
    }
    return sysTaskService.findById(id);
  }

  /**
   * 查询作业分页
   *
   * @param paramMap
   * <p> taskName       - Nullable - Str  - 作业名称
   * <p> description    - Nullable - Str  - 作业名称
   * <p> targetClass    - Nullable - Str  - 作业执行类全名
   * <p> cronExpression - Nullable - Str  - 作业执行周期
   * <p> lastServer     - Nullable - Str  - 最后调用的服务端host，如：192.168.0.1:8055
   * <p> lastTime       - Nullable - Str  - 最后调用时间，如：2017-01-01 11:22:33
   * <p> disabled       - Nullable - bool - 是否已禁用，true/false
   * <p> pageNum        - Nullable - Int  - 当前页数，默认1
   * @return DataPage&lt;SysTask&gt;
   */
  @Override
  @RequestMapping(path = "/task/page", method = RequestMethod.GET,
                  produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
  public DataPage<SysTask> getTaskPage(@RequestParam Map<String, String> paramMap){
    ConditionGroup<SysTask> cg = ConditionGroup.build();
    String taskName = paramMap.get("taskName");
    if(StringUtils.isNotBlank(taskName)){
      cg.addCondition("name", ConditionEnum.OPERATE_EQUAL, taskName);
    }
    String description = paramMap.get("description");
    if(StringUtils.isNotBlank(description)){
      cg.addCondition("description", ConditionEnum.OPERATE_EQUAL, description);
    }
    String targetClass = paramMap.get("targetClass");
    if(StringUtils.isNotBlank(targetClass)){
      cg.addCondition("targetClass", ConditionEnum.OPERATE_EQUAL, targetClass);
    }
    String cronExpression = paramMap.get("cronExpression");
    if(StringUtils.isNotBlank(cronExpression)){
      cg.addCondition("cronExpression", ConditionEnum.OPERATE_EQUAL, cronExpression);
    }
    String lastServer = paramMap.get("lastServer");
    if(StringUtils.isNotBlank(lastServer)){
      cg.addCondition("lastServer", ConditionEnum.OPERATE_EQUAL, lastServer);
    }
    DateTime lastTime = ConvertUtil.dateNullable(paramMap.get("lastTime"), "最后调用时间", null);
    if(lastTime != null){
      cg.addCondition("lastTime", ConditionEnum.OPERATE_EQUAL, lastTime);
    }
    String disabled = paramMap.get("disabled");
    if(StringUtils.isNotBlank(disabled)){
      cg.addCondition("disabled", ConditionEnum.OPERATE_EQUAL, disabled);
    }
    Page<SysTask> page = sysTaskService.findAll(
      cg, Pages.Helper.pageable(
        Pages.Helper.pageNum(paramMap.get("pageNum")),
        Pages.Helper.pageSize(paramMap.get("pageSize")),
        new Sort(new Sort.Order(Sort.Direction.ASC, "disabled"),
                 new Sort.Order(Sort.Direction.DESC, "createTime"))));
    return DataPage.from(page);
  }
  
  @Override
  @RequestMapping(path = "/task/log/page", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
  public DataPage<SysTaskLog> getTaskLogPage(@RequestParam Map<String, String> params) {
    DateTime timeS = ConvertUtil.dateNullable(params.get("timeS"), null, Dates.DATE_TIME_FORMATTER);
    DateTime timeE = ConvertUtil.dateNullable(params.get("timeE"), null, Dates.DATE_TIME_FORMATTER);
    ConditionGroup<SysTaskLog> condition = 
      ConditionGroup.build().addCondition("taskId", ConditionEnum.OPERATE_EQUAL, params.get("taskId"), true)
                    .addCondition("executeSuccess", ConditionEnum.OPERATE_EQUAL, ConvertUtil.convert(params.get("executeSuccess"), Boolean.class), true)
                    .addCondition("taskFireTime", ConditionEnum.OPERATE_GREATER_EQUAL, timeS == null ? null : timeS.toDate(), true)
                    .addCondition("taskFireTime", ConditionEnum.OPERATE_LESS_EQUAL, timeE == null ? null : timeE.toDate(), true);
    return sysTaskLogService.findAll(
      condition, Pages.Helper.pageable(params, new Sort(Sort.Direction.DESC, "taskFireTime")));
  }
  
  @Override
  @RequestMapping(path = "/task/log", method = RequestMethod.DELETE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
  public int deleteSysTaskLog(@RequestParam Map<String, String> params){
    return sysTaskLogService.delete(params);
  }
}
